/**
 * 第013步：项目（Vite）
 * -> npm create vite@latest vite5-vue3-ts5-i18n || npm init vite@latest demo-i18n
 * 
 * 第014步：依赖（NPM）
 * -> npm install
 * // 提前手动创建文件
 * -> .npmrc
 * 第014步：依赖（PNPM）
 * -> pnpm install
 * // 之后自动生成文件（pnpm i --lockfile-only ||	Only pnpm-lock.yaml is updated）
 * -> pnpm-lock.yaml
 * 第014步：依赖（YARN）
 * -> yarn install
 * // 提前手动创建文件
 * -> yarn.lock
 * 
 * 第023步：环境变量（.ENV）
 * -> 开发环境<Development>：
 * -> .env.development
 * -> 测试环境<Testing>：
 * -> .env.testing
 * -> 生产环境<Production>：
 * -> .env.production
 * -> --
 * -> # 开发环境
 * -> NODE_ENV = 'development'
 * -> # 变量必须以（VITE_）为前缀才能暴露给外部读取
 * -> VITE_APP_TITLE = '硅谷甄选运营平台'
 * -> # 基础路径（在Axios二次封装后，基础配置baseURL处通过request调用接口需要使用到它）
 * -> VITE_APP_BASE_API = '/api'
 * -> # 基础服务
 * -> VITE_SERVE = "http://youshang-api.liuweiwei.com"
 * -> .env.development
 * -> .env.production
 * -> .env.testing
 * -> 获取环境变量方法：console.log(import.meta.env)
 * 
 * 第022步：别名（@）
 * -> npm install @types/node -D
 * -> 配置：./vite.config.ts
 * -> import path from "path";
 * -> export default defineConfig((config: ConfigEnv): UserConfig => {
 * ->   return {
 * ->     base: "/",
 * ->     plugins: [vue()],
 * ->     resolve: {
 * ->       alias: {
 * ->         "@": path.resolve("./src"), // 相对路径别名配置，使用@符号代替src
 * ->       },
 * ->     },
 * ->   };
 * -> });
 * -> 配置：./tsconfig.app.json 和 ./tsconfig.node.json
 * -> {
 * ->   "compilerOptions": {
 * ->     "baseUrl": "./", // 解析非相对模块的基地址，默认是当前目录
 * ->     "paths": {
 * ->       "@/*": ["./src/*"] // 路径映射，相对于：baseUrl
 * ->     }
 * ->   },
 * -> }
 * -> 异常：Cannot find module '@/components/HelloWorld.vue' or its corresponding type declarations. Vetur(2307)
 * -> 解决：Vetur插件支持Vue2框架，Volar插件支持Vue3框架，Volar插件已被弃用，欢迎使用Vue-Official插件。
 * -> 重启：退出VSCode工具，重新打开启动项目即可。
 * 
 * 第026步：样式（SCSS）
 * -> 问题：在Vue项目中使用SCSS安装必要的依赖sass和sass-loader
 * -> <style scoped lang="scss"></style>
 * -> 问题：Preprocessor dependency "sass-embedded" not found. Did you install it? Try `npm install sass-embedded -D`.
 * -> npm install sass sass-loader -D
 * -> 配置：./vite.config.ts
 * -> import path from "path";
 * -> import { createSvgIconsPlugin } from "vite-plugin-svg-icons";
 * -> export default defineConfig((config: ConfigEnv): UserConfig => {
 * ->   return {
 * ->     base: "/",
 * ->     plugins: [vue()],
 * ->     resolve: {
 * ->       alias: {
 * ->         "@": path.resolve("./src"), // 相对路径别名配置，使用@符号代替src
 * ->       },
 * ->     },
 * ->     css: {
 * ->       preprocessorOptions: {
 * ->         scss: {
 * ->           javascriptEnabled: true, // sass: ~1.63.6 版本
 * ->           additionalData: `@import "./src/styles/variable.scss";`, // sass: ~1.63.6 版本
 * ->           // additionalData: undefined,
 * ->         },
 * ->       },
 * ->     },
 * ->   };
 * -> });
 * -> 配置：./src/main.ts
 * -> // import "@/style.css";
 * -> // npm install sass sass-loader -D
 * -> import "@/styles/index.scss";
 * -> 配置：./src/styles/index.scss
 * -> // 引入清除默认样式
 * -> @import "./reset.scss";
 * -> // 引入全局变量样式
 * -> @import "./variable.scss";
 * -> 异常1：Deprecation Warning: Sass @import rules are deprecated and will be removed in Dart Sass 3.0.0.
 * -> 解决1：查看Sass官方文档，发现Sass版本高于1.8.0就要把@import 改成@use ，目前使用的是~1.63.6版本
 * -> 异常2：Deprecation Warning: The legacy JS API is deprecated and will be removed in Dart Sass 2.0.0.
 * -> 提示2："sass": "^1.81.1"，意思是自动更新【最新的版本】
 * -> 提示2："sass": "~1.63.6"，意思是只更新【最小的版本】
 * -> 解决2：官方给出的方法是推荐降低到1.63.6
 * -> 提示2：以上问题出自于 Vite 5.x 版本，Vite 6.x 版本已修复此问题。
 * 
 * 第021步：用户界面（Element Plus）
 * -> npm install element-plus @element-plus/icons-vue -S
 * -> 配置：./src/main.ts
 * -> // npm install element-plus -S
 * -> import ElementPlus from "element-plus";
 * -> import "element-plus/dist/index.css";
 * -> import zhCn from "element-plus/es/locale/lang/zh-cn";
 * -> // npm install @element-plus/icons-vue -S
 * -> import * as ElementPlusIconsVue from "@element-plus/icons-vue";
 * -> app.use(ElementPlus, {
 * ->   locale: zhCn
 * -> });
 * -> for (let [key, component] of Object.entries(ElementPlusIconsVue)) {
 * ->   app.component(key, component);
 * -> }
 * 
 * 第030步：路由（VueRouter）初级版
 * -> npm install vue-router -S
 * -> 配置：./src/main.ts
 * -> // npm install vue-router -S
 * -> import router from "@/router/index.ts";
 * -> app.use(router);
 * -> 配置：./src/router/index.ts
 * -> // 第002个包：npm install vue-router -S
 * -> import { createRouter, createWebHistory } from "vue-router";
 * -> // 路由数组（或者）
 * -> // import { constantRoutes } from "./routes";
 * -> // 创建路由实例对象
 * -> let router = createRouter({
 * ->   // 路由模式（createWebHistory()和createWebHashHistory()）
 * ->   history: createWebHistory(),
 * ->   // 路由数组
 * ->   routes: [
 * ->     {
 * ->       path: "/",
 * ->       component: () => import("@/views/layout/index.vue"), // 路由懒加载import
 * ->       name: "Layout", // 命名空间name，命名路由（路由权限控制）
 * ->       meta: {
 * ->         title: "布局页面",
 * ->         icon: "HomeFilled",
 * ->         hidden: false,
 * ->       },
 * ->       redirect: "/home",
 * ->       children: [
 * ->         {
 * ->           path: "/home",
 * ->           component: () => import("@/views/home/index.vue"), // 路由懒加载import
 * ->           name: "Home", // 命名空间name，命名路由（路由权限控制）
 * ->           meta: {
 * ->             title: "公司首页",
 * ->             icon: "HomeFilled",
 * ->             hidden: false,
 * ->           },
 * ->         },
 * ->       ],
 * ->     },
 * ->     {
 * ->       path: "/login",
 * ->       component: () => import("@/views/login/index.vue"), // 路由懒加载import
 * ->       name: "Login", // 命名空间name，命名路由（路由权限控制）
 * ->       meta: {
 * ->         title: "登录页面",
 * ->         icon: "User",
 * ->         hidden: true,
 * ->       },
 * ->     },
 * ->   ],
 * ->   // 路由数组（或者）
 * ->   // routes: constantRoutes,
 * ->   // 滚动行为
 * ->   scrollBehavior() {
 * ->     return {
 * ->       left: 0,
 * ->       top: 0,
 * ->     };
 * ->   },
 * -> });
 * -> // 向外暴露
 * -> export default router;
 * -> 配置（或者）：./router/routes.ts ★★★★★
 * -> export const constantRoutes = [
 * ->   {
 * ->     path: "/",
 * ->     component: () => import("@/views/layout/index.vue"), // 路由懒加载import
 * ->     name: "Layout", // 命名空间name，命名路由（路由权限控制）
 * ->     meta: {
 * ->       title: "布局页面",
 * ->       icon: "HomeFilled",
 * ->       hidden: false,
 * ->     },
 * ->     redirect: "/home",
 * ->     children: [
 * ->       {
 * ->         path: "/home",
 * ->         component: () => import("@/views/home/index.vue"), // 路由懒加载import
 * ->         name: "Home", // 命名空间name，命名路由（路由权限控制）
 * ->         meta: {
 * ->           title: "公司首页",
 * ->           icon: "HomeFilled",
 * ->           hidden: false,
 * ->         },
 * ->       },
 * ->     ],
 * ->   },
 * ->   {
 * ->     path: "/login",
 * ->     component: () => import("@/views/login/index.vue"), // 路由懒加载import
 * ->     name: "Login", // 命名空间name，命名路由（路由权限控制）
 * ->     meta: {
 * ->       title: "登录页面",
 * ->       icon: "User",
 * ->       hidden: true,
 * ->     },
 * ->   },
 * -> ];
 * -> 配置：./src/App.vue
 * -> <template>
 * ->   <div class="app_container">
 * ->     <!-- TODO -> 第一层：在容器中添加路由组件占位符（路由：path: "/"）到页面：component: () => import("@/views/layout/index.vue") -->
 * ->     <router-view></router-view>
 * ->   </div>
 * -> </template>
 * -> <script setup lang="ts">
 * -> // 在 Vue.js 中，router-view 和 router-link 是 Vue Router 提供的两个核心组件，用于实现单页面应用（SPA）中的导航和组件展示。
 * -> //   1. router-view
 * -> //     router-view 是一个特殊的 Vue 组件，用于渲染通过 Vue Router 定义的路由规则中的组件。它会根据当前的 URL 渲染对应的组件。
 * -> //     当 URL 改变时，router-view 会自动更新渲染的组件，而无需重新加载整个页面。
 * -> //     通常，router-view 会被放置在应用的布局组件中，作为页面内容的主要展示区域。
 * -> //   2. router-link
 * -> //     router-link 是 Vue Router 提供的用于创建链接的组件。它相当于 HTML 标签中的 <a> 标签，但可以更智能地处理导航。
 * -> //     使用 router-link 可以避免页面的完全重载，从而提高用户体验和性能。
 * -> //     router-link 的 to 属性用于指定链接的目标地址，可以是字符串或对象形式。
 * -> // 示例代码
 * -> //   1. 假设你有以下的路由配置
 * -> //     let routes = [
 * -> //       { path: "./", component: HomeComponent },
 * -> //       { path: "./about", component: AboutComponent },
 * -> //       { path: "./user/:id", component: UserComponent },
 * -> //     ];
 * -> //   2. 在你的布局组件中，你可以这样使用 router-view 和 router-link
 * -> //     <template>
 * -> //       <div id="app">
 * -> //         <nav>
 * -> //           <router-link to="/">Home</router-link>
 * -> //           <router-link to="/about">About</router-link>
 * -> //           <router-view></router-view>
 * -> //         </nav>
 * -> //       </div>
 * -> //     </template>
 * -> // 在这个例子中，router-link 用于创建导航链接，而 router-view 用于展示根据当前 URL 动态加载的组件。
 * -> </script>
 * -> <style scoped lang="scss"></style>
 * -> 配置：./src/views/layout/index.vue
 * -> <template>
 * ->   <div class="layout_container">
 * ->     <h1>布局页面：上面一块...</h1>
 * ->     <h1>布局页面：左边一块...</h1>
 * ->     <Domain></Domain>
 * ->   </div>
 * -> </template>
 * -> <script setup lang="ts">
 * ->   import Domain from "@/views/layout/domain/index.vue";
 * -> </script>
 * -> <style scoped lang="scss"></style>
 * -> 配置：./src/views/layout/domain/index.vue
 * -> <template>
 * ->   <div class="domain_container">
 * ->     <!-- 0. RouterView(渲染路由组件) && Transition(过渡动画组件) && Component(动态组件) || KeepAlive(动态切换缓存组件) -->
 * ->     <!-- 
 * ->     <router-view v-slot="{ Component, route }">
 * ->       <keep-alive>
 * ->         <transition :name="route.meta.transition || 'fade'" mode="out-in" appear>
 * ->           <component :is="Component" key="route.path" view-prop="value"></component>
 * ->         </transition>
 * ->       </keep-alive>
 * ->     </router-view>
 * ->     -->
 * ->     <!-- 0. RouterView插槽 + Transition过渡动画 + Component动态组件 -->
 * ->     <!-- 1. TODO -> 第二层：在容器中添加路由组件占位符（路由：path: "/home"）到页面：component: () => import("@/views/home/index.vue") -->
 * ->     <!-- 1.1. RouterView 插槽：显示用户所在的当前路由的组件 -->
 * ->     <!-- 1.2. v-slot="{ Component, route }" 接收 prop 的默认插槽并解构 -->
 * ->     <router-view v-slot="{ Component, route }">
 * ->       <!-- 2. 组件间过渡 -->
 * ->       <!-- 2.1. 内置组件：Transition name="fade" mode="out-in" appear -->
 * ->       <transition name="route.meta.transition || fade" mode="out-in" appear>
 * ->         <!-- 2.2. 动态组件 -->
 * ->         <component :is="Component" :key="route.path" view-prop="value"></component>
 * ->       </transition>
 * ->     </router-view>
 * ->   </div>
 * -> </template>
 * -> <script setup lang="ts"></script>
 * -> <style scoped lang="scss">
 * -> .fade-enter-active, .fade-leave-active {
 * ->   transition: opacity 0.5s ease;
 * -> }
 * -> .fade-enter-from, .fade-leave-to {
 * ->   opacity: 0;
 * -> }
 * -> </style>
 * -> 配置：./src/views/home/index.vue
 * -> <template>
 * ->   <div class="home_container">
 * ->     <div class="mb-4">
 * ->       <el-button @click="router.push('/login')">登录页面</el-button>
 * ->       <el-button @click="() => router.push('/login')">登录页面</el-button>
 * ->     </div>
 * ->   </div>
 * -> </template>
 * -> <script setup lang="ts">
 * ->   import { useRouter } from "vue-router";
 * ->   let router = useRouter();
 * -> </script>
 * -> <style scoped lang="scss"></style>
 * -> 配置：./src/views/login/index.vue
 * -> <template>
 * ->   <div class="home_container">
 * ->     <div class="mb-4">
 * ->       <el-button @click="router.push('/home')">公司首页</el-button>
 * ->       <el-button @click="() => router.push('/home')">公司首页</el-button>
 * ->     </div>
 * ->   </div>
 * -> </template>
 * -> <script setup lang="ts">
 * ->   import { useRouter } from "vue-router";
 * ->   let router = useRouter();
 * -> </script>
 * -> <style scoped lang="scss"></style>
 * 
 * 第024-025步：图标（SVG）
 * -> npm install vite-plugin-svg-icons -D
 * -> 配置：./vite.config.ts
 * -> import path from "path";
 * -> import { createSvgIconsPlugin } from "vite-plugin-svg-icons";
 * -> export default defineConfig((config: ConfigEnv): UserConfig => {
 * ->   let envDir = process.cwd();
 * ->   return {
 * ->     base: "/",
 * ->     plugins: [
 * ->       vue(),
 * ->       createSvgIconsPlugin({
 * ->         iconDirs: [path.resolve(envDir, "src/assets/icons")],
 * ->         symbolId: "icon-[dir]-[name]",
 * ->       }),
 * ->     ],
 * ->     resolve: {
 * ->       alias: {
 * ->         "@": path.resolve("./src"), // 相对路径别名配置，使用@符号代替src
 * ->       },
 * ->     },
 * ->     css: {
 * ->       preprocessorOptions: {
 * ->         scss: {
 * ->           javascriptEnabled: true, // sass: ~1.63.6 版本
 * ->           additionalData: `@import "./src/styles/variable.scss";`, // sass: ~1.63.6 版本
 * ->           // additionalData: undefined,
 * ->         },
 * ->       },
 * ->     },
 * ->   };
 * -> });
 * -> 配置：./src/matin.ts
 * -> // npm install vite-plugin-svg-icons -D
 * -> // 插件所需配置代码
 * -> import "virtual:svg-icons-register";
 * -> import SvgIcons from '@/components/index.ts';
 * -> app.use(SvgIcons);
 * -> 配置：./components/index.ts
 * -> import SvgIcon from "@/components/SvgIcon/index.vue";
 * -> let allGlobalComponents = { SvgIcon };
 * -> export default {
 * ->   install(app) {
 * ->     Object.keys(allGlobalComponents).forEach((key) => {
 * ->       app.component(key, allGlobalComponents[key]);
 * ->     });
 * ->   },
 * -> };
 * -> 配置：./components/SvgIcon/index.vue
 * -> <template>
 * ->   // svg：图标外层容器节点，内部需要与 use 标签结合使用。
 * ->   // xlink:href：执行用哪一个图标，属性值务必使用 #icon- 图标名字。
 * ->   // use：标签，fill 属性可以设置图标的颜色。
 * ->   <svg :style="{ width, height }">
 * ->     <use :xlink:href="symbolId" rel="external nofollow" :fill="color"></use>
 * ->   </svg>
 * -> </template>
 * -> <script name="SvgIcon" setup lang="ts">
 * -> import { computed } from "vue";
 * -> let props = defineProps({
 * ->   prefix: {
 * ->     type: String,
 * ->     default: "icon",
 * ->   },
 * ->   name: String,
 * ->   iconClass: {
 * ->     type: String,
 * ->     required: false,
 * ->   },
 * ->   color: {
 * ->     type: String,
 * ->     default: "#000",
 * ->   },
 * ->   width: {
 * ->     type: String,
 * ->     default: "16px",
 * ->   },
 * ->   height: {
 * ->     type: String,
 * ->     default: "16px",
 * ->   },
 * -> });
 * -> let symbolId = computed(() => `#${props.prefix}-${props.name}`);
 * -> </script>
 * -> <style scoped lang="scss"></style>
 * -> 使用：
 * -> <svg-icon name="home" width="24px" height="24px"></svg-icon>
 * -> <svg-icon name="home"></svg-icon>
 * 
 * 第027-028-029步：数据（MockJS）
 * -> 安装依赖：https://www.npmjs.com/package/vite-plugin-mock
 * -> npm install -D vite-plugin-mock mockjs
 * -> npm install -S axios
 * -> npm install -S pinia
 * -> 配置：./vite.config.ts
 * -> import path from "path";
 * -> import { createSvgIconsPlugin } from "vite-plugin-svg-icons";
 * -> import { viteMockServe } from "vite-plugin-mock";
 * -> export default defineConfig((config: ConfigEnv): UserConfig => {
 * ->   const command = config.command;
 * ->   const mode = config.mode;
 * ->   return {
 * ->     base: "/",
 * ->     plugins: [
 * ->       vue(),
 * ->       createSvgIconsPlugin({
 * ->         iconDirs: [path.resolve(envDir, "src/assets/icons")],
 * ->         symbolId: "icon-[dir]-[name]",
 * ->       }),
 * ->       viteMockServe({
 * ->         mockPath: "mock",
 * ->         enable: command === "serve",
 * ->       }),
 * ->     ],
 * ->     resolve: {
 * ->       alias: {
 * ->         "@": path.resolve("./src"), // 相对路径别名配置，使用@符号代替src
 * ->       },
 * ->     },
 * ->     css: {
 * ->       preprocessorOptions: {
 * ->         scss: {
 * ->           javascriptEnabled: true, // sass: ~1.63.6 版本
 * ->           additionalData: `@import "./src/styles/variable.scss";`, // sass: ~1.63.6 版本
 * ->           // additionalData: undefined,
 * ->         },
 * ->       },
 * ->     },
 * ->   };
 * -> });
 * -> 在根目录创建mock文件夹，去创建需要MockJS数据与接口。
 * -> 配置：./mock/table_user.ts
 * -> function createUserList() {
 * ->   return [
 * ->     {
 * ->       ...
 * ->     },
 * ->     {
 * ->       ...
 * ->     },
 * ->   ];
 * -> }
 * -> export default [
 * ->   {
 * ->     url: "/api/user/login",
 * ->     method: "post",
 * ->     response: ({ body }) => {
 * ->       let { username, password } = body;
 * ->       let checkUser = createUserList().find((item) => item.username === username && item.password === password);
 * ->       if (!checkUser) {} else { return {}; }
 * ->     },
 * ->   },
 * ->   {
 * ->     url: "/api/user/info",
 * ->     method: "get",
 * ->     response: (request) => {
 * ->       let token = request.headers.token;
 * ->       let checkUser = createUserList().find((item) => item.token === token);
 * ->       if (!checkUser) {} else { return {}; }
 * ->     },
 * ->   },
 * ->   {
 * ->     url: "/api/user/logout",
 * ->     method: "get",
 * ->     response: (request) => {
 * ->       let token = request.headers.token;
 * ->       let checkUser = createUserList().find((item) => item.token === token);
 * ->       if (!checkUser) {} else { return {}; }
 * ->     },
 * ->   },
 * -> ];
 * -> 原生：Axios原生方式
 * -> npm install -S axios
 * -> ./src/views/home/index.vue
 * -> <el-button @click="userLoginAxios">Axios.userLogin</el-button>
 * -> <el-button @click="userInfoAxios">Axios.userInfo</el-button>
 * -> <el-button @click="userLogoutAxios">Axios.userLogout</el-button>
 * -> 源码：
 * -> export interface AxiosRequestConfig<D = any> {
 * ->   url?: string;
 * ->   method?: Method | string;
 * ->   headers?: (RawAxiosRequestHeaders & MethodsHeaders) | AxiosHeaders;
 * ->   params?: any;
 * ->   data?: D;
 * -> }
 * -> export interface AxiosInstance extends Axios {
 * ->   <T = any, R = AxiosResponse<T>, D = any>(config: AxiosRequestConfig<D>): Promise<R>;
 * -> }
 * -> // 测试一：Axios请求MockJS数据（原生方式）
 * -> // Axios [return] data: {data: {…}, status: 200, statusText: 'OK', headers: AxiosHeaders, config: {…}, …}
 * -> import axios from "axios";
 * -> const userLoginAxios = async () => {
 * ->   await axios({
 * ->     url: "/api/user/login",
 * ->     method: "post",
 * ->     data: {
 * ->       username: "admin",
 * ->       password: "111111",
 * ->     },
 * ->     headers: {
 * ->       "Content-Type": "application/json", // JSON格式
 * ->       // 'Content-Type': 'application/x-www-form-urlencoded', // 表单格式
 * ->     },
 * ->   })
 * ->     .then((result: any) => {
 * ->       console.log("Axios [return] data:", result);
 * ->     })
 * ->     .catch((error: any) => {
 * ->       console.error("Axios [return] error:", error);
 * ->     });
 * -> };
 * -> const userInfoAxios = async () => {
 * ->   await axios({
 * ->     url: "/api/user/info",
 * ->     method: "get",
 * ->     params: {
 * ->       username: "admin",
 * ->       password: "111111",
 * ->     },
 * ->     headers: {
 * ->       "X-Requested-With": "XMLHttpRequest",
 * ->       token: "Admin Token",
 * ->     },
 * ->   })
 * ->     .then((result: any) => {
 * ->       console.log("Axios [return] data:", result);
 * ->     })
 * ->     .catch((error: any) => {
 * ->       console.error("Axios [return] error:", error);
 * ->     });
 * -> };
 * -> const userLogoutAxios = async () => {
 * ->   await axios({
 * ->     url: "/api/user/logout",
 * ->     method: "get",
 * ->     params: {
 * ->       username: "admin",
 * ->       password: "111111",
 * ->     },
 * ->     headers: {
 * ->       "X-Requested-With": "XMLHttpRequest",
 * ->       token: "Admin Token",
 * ->     },
 * ->   })
 * ->     .then((result: any) => {
 * ->       console.log("Axios [return] data:", result);
 * ->     })
 * ->     .catch((error: any) => {
 * ->       console.error("Axios [return] error:", error);
 * ->     });
 * -> };
 * -> 封装：Axios二次封装
 * -> npm install -S axios
 * -> 使用Axios网络请求库插件发送网络请求，进行Axios二次封装。
 * -> 使用请求拦截器：可以在请求拦截器中处理一些业务（比如：开始进度条，请求头携带公共参数Token），
 * -> 使用响应拦截器：可以在响应拦截器中处理一些业务（比如：进度条结束，简化服务器响应的数据，处理Http网络错误），
 * -> 在根目录下创建目录：./utils/request.ts ★★★★★
 * -> import axios from "axios";
 * -> import { ElMessage } from "element-plus";
 * -> // import useUserStore from "@/store/modules/user";
 * -> let request = axios.create({
 * ->   baseURL: import.meta.env.VITE_APP_BASE_API,
 * ->   timeout: 5000,
 * -> });
 * -> request.interceptors.request.use(
 * ->   // 请求回调函数
 * ->   (config) => {
 * ->     console.log('请求成功:', config);
 * ->     // let userStore = useUserStore();
 * ->     // if (userStore.token) {
 * ->     //   // 写法一
 * ->     //   // config.headers["token"] = userStore.token;
 * ->     //   // 写法二
 * ->     //   config.headers.token = userStore.token;
 * ->     // }
 * ->     return config;
 * ->   },
 * ->   // 请求回调函数
 * ->   (error) => {
 * ->     console.log('请求错误:', error);
 * ->     return Promise.reject(error);
 * ->   },
 * -> );
 * -> request.interceptors.response.use(
 * ->   // 成功回调函数
 * ->   (response) => {
 * ->     console.log('响应成功:', response);
 * ->     return response.data;
 * ->   },
 * ->   // 失败回调函数
 * ->   (error) => {
 * ->     console.log('响应失败:', error);
 * ->     let message = "";
 * ->     let status = error.response.status;
 * ->     switch (status) {
 * ->       case 401:
 * ->         message = "TOKEN过期";
 * ->         break;
 * ->       case 403:
 * ->         message = "无权访问";
 * ->         break;
 * ->       case 404:
 * ->         message = "请求地址错误";
 * ->         break;
 * ->       case 500:
 * ->         message = "服务器出现问题";
 * ->         break;
 * ->       default:
 * ->         message = "网络出现问题";
 * ->         break;
 * ->     }
 * ->     ElMessage({
 * ->       type: "error",
 * ->       message,
 * ->     });
 * ->     return Promise.reject(error);
 * ->   }
 * -> );
 * -> export default request;
 * -> // 以上日志信息，在下面二次封装通过request调用接口时会被打印。
 * -> ./src/views/home/index.vue
 * -> <el-button @click="userLoginRequest">Request.userLogin</el-button>
 * -> <el-button @click="userInfoRequest">Request.userInfo</el-button>
 * -> <el-button @click="userLogoutRequest">Request.userLogout</el-button>
 * -> 源码：
 * -> export interface AxiosRequestConfig<D = any> {
 * ->   url?: string;
 * ->   method?: Method | string;
 * ->   headers?: (RawAxiosRequestHeaders & MethodsHeaders) | AxiosHeaders;
 * ->   params?: any;
 * ->   data?: D;
 * -> }
 * -> export class Axios {
 * ->   request<T = any, R = AxiosResponse<T>, D = any>(config: AxiosRequestConfig<D>): Promise<R>;
 * ->   get<T = any, R = AxiosResponse<T>, D = any>(url: string, config?: AxiosRequestConfig<D>): Promise<R>;
 * ->   post<T = any, R = AxiosResponse<T>, D = any>(url: string, data?: D, config?: AxiosRequestConfig<D>): Promise<R>;
 * -> }
 * -> export interface AxiosInstance extends Axios {
 * ->   <T = any, R = AxiosResponse<T>, D = any>(config: AxiosRequestConfig<D>): Promise<R>;
 * -> }
 * -> // 测试二：Axios请求MockJS数据（二次封装）
 * -> // 请求成功: {transitional: {…}, adapter: Array(3), transformRequest: Array(1), transformResponse: Array(1), timeout: 5000, …}
 * -> // 响应成功: {data: {…}, status: 200, statusText: 'OK', headers: AxiosHeaders, config: {…}, …}
 * -> // Request [return] data: {code: 200, data: 'Admin Token', message: 'success', okay: true}
 * -> import request from "@/utils/request.ts";
 * -> const userLoginRequest = async () => {
 * ->   await request({
 * ->     url: "/user/login",
 * ->     method: "post",
 * ->     data: {
 * ->       username: "admin",
 * ->       password: "111111",
 * ->     },
 * ->     headers: {
 * ->       "Content-Type": "application/json", // JSON格式
 * ->       // 'Content-Type': 'application/x-www-form-urlencoded', // 表单格式
 * ->     },
 * ->   })
 * ->     .then((result: any) => {
 * ->       console.log("Request [return] data:", result);
 * ->     })
 * ->     .catch((error: any) => {
 * ->       console.error("Request [return] error:", error);
 * ->     });
 * -> };
 * -> const userInfoRequest = async () => {
 * ->   await request({
 * ->     url: "/user/info",
 * ->     method: "get",
 * ->     params: {
 * ->       username: "admin",
 * ->       password: "111111",
 * ->     },
 * ->     headers: {
 * ->       "X-Requested-With": "XMLHttpRequest",
 * ->       token: "Admin Token",
 * ->     },
 * ->   })
 * ->     .then((result: any) => {
 * ->       console.log("Request [return] data:", result);
 * ->     })
 * ->     .catch((error: any) => {
 * ->       console.error("Request [return] error:", error);
 * ->     });
 * -> };
 * -> const userLogoutRequest = async () => {
 * ->   await request({
 * ->     url: "/user/logout",
 * ->     method: "get",
 * ->     params: {
 * ->       username: "admin",
 * ->       password: "111111",
 * ->     },
 * ->     headers: {
 * ->       "X-Requested-With": "XMLHttpRequest",
 * ->       token: "Admin Token",
 * ->     },
 * ->   })
 * ->     .then((result: any) => {
 * ->       console.log("Request [return] data:", result);
 * ->     })
 * ->     .catch((error: any) => {
 * ->       console.error("Request [return] error:", error);
 * ->     });
 * -> };
 * -> 管理：Api接口统一管理
 * -> npm install -S axios
 * -> 前提：./utils/request.ts
 * -> 配置：./api/user/types.ts ★★★★★
 * -> import type { RouteRecordRaw } from "vue-router";
 * -> export interface UserState {
 * ->   token: string | null;
 * ->   menuRoutes: RouteRecordRaw[];
 * ->   avatar: string | null;
 * ->   username: string | null;
 * ->   buttons: [];
 * -> }
 * -> 配置：./api/user/index.ts ★★★★★
 * -> import { AxiosPromise } from "axios";
 * -> import request from "@/utils/request.ts";
 * -> // 第二种：Axios请求MockJS数据（二次封装）Api优化版
 * -> export const userLoginApi = (data: any, headers?: any) => {
 * ->   return request.post<any, any>('/user/login', data, { headers });
 * -> };
 * -> // 第二种：Axios请求MockJS数据（二次封装）Api优化版
 * -> export const userInfoApi = (params?: any, headers?: any) => {
 * ->   return request.get<any, any>('/user/info', { params, headers });
 * -> };
 * -> // 第二种：Axios请求MockJS数据（二次封装）Api优化版
 * -> export const userLogoutApi = (params?: any, headers?: any) => {
 * ->   return request.get<any, any>('/user/logout', { params, headers });
 * -> };
 * -> // 以上日志信息，在下面二次封装通过request调用接口时会被打印。
 * -> ./src/views/home/index.vue
 * -> <el-button @click="userLoginRequestApi">Api.Request.userLogin</el-button>
 * -> <el-button @click="userInfoRequestApi">Api.Request.userInfo</el-button>
 * -> <el-button @click="userLogoutRequestApi">Api.Request.userLogout</el-button>
 * -> 源码：
 * -> export interface AxiosRequestConfig<D = any> {
 * ->   url?: string;
 * ->   method?: Method | string;
 * ->   headers?: (RawAxiosRequestHeaders & MethodsHeaders) | AxiosHeaders;
 * ->   params?: any;
 * ->   data?: D;
 * -> }
 * -> export class Axios {
 * ->   request<T = any, R = AxiosResponse<T>, D = any>(config: AxiosRequestConfig<D>): Promise<R>;
 * ->   get<T = any, R = AxiosResponse<T>, D = any>(url: string, config?: AxiosRequestConfig<D>): Promise<R>;
 * ->   post<T = any, R = AxiosResponse<T>, D = any>(url: string, data?: D, config?: AxiosRequestConfig<D>): Promise<R>;
 * -> }
 * -> export interface AxiosInstance extends Axios {
 * ->   <T = any, R = AxiosResponse<T>, D = any>(config: AxiosRequestConfig<D>): Promise<R>;
 * -> }
 * -> // 测试三：Axios请求MockJS数据（二次封装）Api优化版
 * -> // 请求成功: {transitional: {…}, adapter: Array(3), transformRequest: Array(1), transformResponse: Array(1), timeout: 5000, …}
 * -> // 响应成功: {data: {…}, status: 200, statusText: 'OK', headers: AxiosHeaders, config: {…}, …}
 * -> // Api [return] data: {code: 200, data: 'Admin Token', message: 'success', okay: true}
 * -> import { userLoginApi, userInfoApi, userLogoutApi } from "@/api/user/index.ts";
 * -> const userLoginRequestApi = async () => {
 * ->   let data = {
 * ->     username: "admin",
 * ->     password: "111111",
 * ->   };
 * ->   let headers = {
 * ->     "Content-Type": "application/json", // JSON格式
 * ->     // 'Content-Type': 'application/x-www-form-urlencoded', // 表单格式
 * ->   };
 * ->   let res = await userLoginApi(data, headers);
 * ->   console.log("Api [return] data:", res);
 * -> };
 * -> const userInfoRequestApi = async () => {
 * ->   let params = {
 * ->     username: "admin",
 * ->     password: "111111",
 * ->   };
 * ->   let headers = {
 * ->     "X-Requested-With": "XMLHttpRequest",
 * ->     token: "Admin Token",
 * ->   };
 * ->   let res = await userInfoApi(params, headers);
 * ->   console.log("Api [return] data:", res);
 * -> };
 * -> const userLogoutRequestApi = async () => {
 * ->   let params = {
 * ->     username: "admin",
 * ->     password: "111111",
 * ->   };
 * ->   let headers = {
 * ->     "X-Requested-With": "XMLHttpRequest",
 * ->     token: "Admin Token",
 * ->   };
 * ->   let res = await userLogoutApi(params, headers);
 * ->   console.log("Api [return] data:", res);
 * -> };
 * -> 扩展：Pinia状态管理库
 * -> npm install -S pinia
 * -> 前提：./utils/request.ts
 * -> 前提：./api/user/types.ts
 * -> 前提：./api/user/index.ts
 * -> 前提：./router/routes.ts
 * -> 配置：./src/main.ts
 * -> // npm install vue-router -S
 * -> import router from "@/router/index.ts";
 * -> // npm install pinia -S
 * -> import pinia from "@/store/index.ts";
 * -> app.use(router);
 * -> app.use(pinia);
 * -> 配置：./store/index.ts
 * -> import { createPinia } from "pinia";
 * -> const pinia = createPinia();
 * -> export default pinia;
 * -> 配置：./store/modules/user.ts
 * -> import { userLoginApi, userInfoApi, userLogoutApi } from "@/api/user/index.ts";
 * -> import { UserState } from "@/api/user/types.ts";
 * -> import { constantRoutes } from "@/router/routes.ts";
 * -> import { defineStore } from "pinia";
 * -> const useUserStore = defineStore("User", {
 * ->   state: (): UserState => {
 * ->     return {
 * ->       token: "",
 * ->       menuRoutes: constantRoutes,
 * ->       username: "",
 * ->       avatar: "",
 * ->       buttons: [],
 * ->     };
 * ->   },
 * ->   actions: {
 * ->     // 第二种：Axios请求MockJS数据（二次封装）Api优化版之Pinia状态管理库版
 * ->     async userLoginAction(data: any, headers?: any) {
 * ->       return await userLoginApi(data, headers);
 * ->     },
 * ->     // 第二种：Axios请求MockJS数据（二次封装）Api优化版之Pinia状态管理库版
 * ->     async userInfoAction(params?: any, headers?: any) {
 * ->       return await userInfoApi(params, headers);
 * ->     },
 * ->     // 第二种：Axios请求MockJS数据（二次封装）Api优化版之Pinia状态管理库版
 * ->     async userLogoutAction(params?: any, headers?: any) {
 * ->       return await userLogoutApi(params, headers);
 * ->     },
 * ->   },
 * ->   getters: {},
 * -> });
 * -> export default useUserStore;
 * -> // 以上日志信息，在下面二次封装通过request调用接口时会被打印。
 * -> ./src/views/home/index.vue
 * -> <el-button @click="userLoginRequestApiPinia">Pinia.Api.Request.userLogin</el-button>
 * -> <el-button @click="userInfoRequestApiPinia">Pinia.Api.Request.userInfo</el-button>
 * -> <el-button @click="userLogoutRequestApiPinia">Pinia.Api.Request.userLogout</el-button>
 * -> 源码：
 * -> export interface AxiosRequestConfig<D = any> {
 * ->   url?: string;
 * ->   method?: Method | string;
 * ->   headers?: (RawAxiosRequestHeaders & MethodsHeaders) | AxiosHeaders;
 * ->   params?: any;
 * ->   data?: D;
 * -> }
 * -> export class Axios {
 * ->   request<T = any, R = AxiosResponse<T>, D = any>(config: AxiosRequestConfig<D>): Promise<R>;
 * ->   get<T = any, R = AxiosResponse<T>, D = any>(url: string, config?: AxiosRequestConfig<D>): Promise<R>;
 * ->   post<T = any, R = AxiosResponse<T>, D = any>(url: string, data?: D, config?: AxiosRequestConfig<D>): Promise<R>;
 * -> }
 * -> export interface AxiosInstance extends Axios {
 * ->   <T = any, R = AxiosResponse<T>, D = any>(config: AxiosRequestConfig<D>): Promise<R>;
 * -> }
 * -> // 测试四：Axios请求MockJS数据（二次封装）Api优化版之经过Pinia状态管理库版
 * -> // 请求成功: {transitional: {…}, adapter: Array(3), transformRequest: Array(1), transformResponse: Array(1), timeout: 5000, …}
 * -> // 响应成功: {data: {…}, status: 200, statusText: 'OK', headers: AxiosHeaders, config: {…}, …}
 * -> // Pinia [return] data: {code: 200, data: 'Admin Token', message: 'success', okay: true}
 * -> import useUserStore from '@/store/modules/user';
 * -> let userStore = useUserStore();
 * -> let userLoginRequestApiPinia = async () => {
 * ->   let data = {
 * ->     username: "admin",
 * ->     password: "111111",
 * ->   };
 * ->   let headers = {
 * ->     "Content-Type": "application/json", // JSON格式
 * ->     // 'Content-Type': 'application/x-www-form-urlencoded', // 表单格式
 * ->   };
 * ->   let res = await userStore.userLoginAction(data, headers);
 * ->   console.log("Pinia [return] data:", res);
 * -> };
 * -> const userInfoRequestApiPinia = async () => {
 * ->   let params = {
 * ->     username: "admin",
 * ->     password: "111111",
 * ->   };
 * ->   let headers = {
 * ->     "X-Requested-With": "XMLHttpRequest",
 * ->     token: "Admin Token",
 * ->   };
 * ->   let res = await userStore.userInfoAction(params, headers);
 * ->   console.log("Pinia [return] data:", res);
 * -> };
 * -> const userLogoutRequestApiPinia = async () => {
 * ->   let params = {
 * ->     username: "admin",
 * ->     password: "111111",
 * ->   };
 * ->   let headers = {
 * ->     "X-Requested-With": "XMLHttpRequest",
 * ->     token: "Admin Token",
 * ->   };
 * ->   let res = await userStore.userLogoutAction(params, headers);
 * ->   console.log("Pinia [return] data:", res);
 * -> };
 * 
 * 第032步：状态管理库（Pinia）
 * -> npm install pinia -S
 * -> 配置：./src/main.ts
 * -> // npm install vue-router -S
 * -> import router from "@/router/index.ts";
 * -> // npm install pinia -S
 * -> import pinia from "@/store/index.ts";
 * -> app.use(router);
 * -> app.use(pinia);
 * -> 配置：./src/store/index.ts
 * -> // 第002个包：npm install pinia -S
 * -> // 引入仓库
 * -> import { createPinia } from "pinia";
 * -> // 创建仓库
 * -> let pinia = createPinia();
 * -> // 对外暴露
 * -> export default pinia;
 * -> 扩展配置：./src/utils/request.ts ★★★★★
 * -> import axios from "axios";
 * -> import { ElMessage } from "element-plus";
 * -> // import useUserStore from "@/store/modules/user";
 * -> let request = axios.create({
 * ->   baseURL: import.meta.env.VITE_APP_BASE_API,
 * ->   timeout: 5000,
 * -> });
 * -> request.interceptors.request.use(
 * ->   // 请求回调函数
 * ->   (config) => {
 * ->     console.log('请求成功:', config);
 * ->     // let userStore = useUserStore();
 * ->     // if (userStore.token) {
 * ->     //   // 写法一
 * ->     //   // config.headers["token"] = userStore.token;
 * ->     //   // 写法二
 * ->     //   config.headers.token = userStore.token;
 * ->     // }
 * ->     return config;
 * ->   },
 * ->   // 请求回调函数
 * ->   (error) => {
 * ->     console.log('请求错误:', error);
 * ->     return Promise.reject(error);
 * ->   },
 * -> );
 * -> request.interceptors.response.use(
 * ->   // 成功回调函数
 * ->   (response) => {
 * ->     console.log('响应成功:', response);
 * ->     return response.data;
 * ->   },
 * ->   // 失败回调函数
 * ->   (error) => {
 * ->     console.log('响应失败:', error);
 * ->     let message = "";
 * ->     let status = error.response.status;
 * ->     switch (status) {
 * ->       case 401:
 * ->         message = "TOKEN过期";
 * ->         break;
 * ->       case 403:
 * ->         message = "无权访问";
 * ->         break;
 * ->       case 404:
 * ->         message = "请求地址错误";
 * ->         break;
 * ->       case 500:
 * ->         message = "服务器出现问题";
 * ->         break;
 * ->       default:
 * ->         message = "网络出现问题";
 * ->         break;
 * ->     }
 * ->     ElMessage({
 * ->       type: "error",
 * ->       message,
 * ->     });
 * ->     return Promise.reject(error);
 * ->   }
 * -> );
 * -> export default request;
 * -> 扩展配置：./src/router/routers.ts ★★★★★
 * -> export const constantRoutes = [
 * ->   {
 * ->     path: "/",
 * ->     component: () => import("@/views/layout/index.vue"),
 * ->     name: "Layout",
 * ->     meta: {
 * ->       title: "布局页面",
 * ->       icon: "HomeFilled",
 * ->       hidden: false,
 * ->     },
 * ->     redirect: "/home",
 * ->     children: [
 * ->       {
 * ->         path: "/home",
 * ->         component: () => import("@/views/home/index.vue"),
 * ->         name: "Home",
 * ->         meta: {
 * ->           title: "公司首页",
 * ->           icon: "HomeFilled",
 * ->           hidden: false,
 * ->         },
 * ->       },
 * ->     ],
 * ->   },
 * ->   {
 * ->     path: "/login",
 * ->     component: () => import("@/views/login/index.vue"),
 * ->     name: "Login",
 * ->     meta: {
 * ->       title: "登录页面",
 * ->       icon: "User",
 * ->       hidden: true,
 * ->     },
 * ->   },
 * -> ];
 * -> 扩展配置：./src/api/user/user.ts ★★★★★
 * -> import type { RouteRecordRaw } from "vue-router";
 * -> export interface UserState {
 * ->   token: string | null;
 * ->   menuRoutes: RouteRecordRaw[];
 * ->   avatar: string | null;
 * ->   username: string | null;
 * ->   buttons: [];
 * -> }
 * -> 扩展配置：./src/api/user/index.ts ★★★★★
 * -> import { AxiosPromise } from "axios";
 * -> import request from "@/utils/request.ts";
 * -> // 第二种：Axios请求MockJS数据（二次封装）优化版
 * -> export const userLoginApi = (data: any, headers?: any) => {
 * ->   return request.post<any, any>('/user/login', data, {headers});
 * -> };
 * -> // 第二种：Axios请求MockJS数据（二次封装）优化版
 * -> export const userInfoApi = (params?: any, headers?: any) => {
 * ->   return request.get<any, any>('/user/info', {params, headers});
 * -> };
 * -> // 第二种：Axios请求MockJS数据（二次封装）优化版
 * -> export const userLogoutApi = (params?: any, headers?: any) => {
 * ->   return request.get<any, any>('/user/logout', {params, headers});
 * -> };
 * -> 配置：./src/store/modules/user.ts
 * -> import { UserState } from "@/api/user/types";
 * -> import { constantRoutes } from "@/router/routes";
 * -> import { defineStore } from "pinia";
 * -> // 引入Api
 * -> import { userLoginApi, userInfoApi, userLogoutApi } from "@/api/user/index.ts";
 * -> // 创建用户小仓库
 * -> const useUserStore = defineStore("User", {
 * ->   // 小仓库存储数据地方
 * ->   state: (): UserState => {
 * ->     return {
 * ->       token: "",
 * ->       menuRoutes: constantRoutes,
 * ->       username: "",
 * ->       avatar: "",
 * ->       buttons: [],
 * ->     };
 * ->   },
 * ->   // 异步逻辑的地方
 * ->   actions: {
 * ->     // 登录接口Api：使用Axios二次封装调用MockJS数据是否成功（优化版）
 * ->     async userLoginAction(data: any, headers?: any) {
 * ->       return await userLoginApi(data, headers);
 * ->     },
 * ->     // 用户接口Api：使用Axios二次封装调用MockJS数据是否成功（优化版）
 * ->     async userInfoAction(params?: any, headers?: any) {
 * ->       return await userInfoApi(params, headers);
 * ->     },
 * ->     // 退出接口Api：使用Axios二次封装调用MockJS数据是否成功（优化版）
 * ->     async userLogoutAction(params?: any, headers?: any) {
 * ->       return await userLogoutApi(params, headers);
 * ->     },
 * ->   },
 * ->   getters: {},
 * -> });
 * -> // 对外暴露获取小仓库的方法
 * -> export default useUserStore;
 * -> // 以上日志信息，在下面二次封装通过request调用接口时会被打印。
 * -> ./src/views/home/index.vue
 * -> <el-button @click="userLoginRequestApiPinia">Pinia.Api.Request.userLogin</el-button>
 * -> <el-button @click="userInfoRequestApiPinia">Pinia.Api.Request.userInfo</el-button>
 * -> <el-button @click="userLogoutRequestApiPinia">Pinia.Api.Request.userLogout</el-button>
 * -> 源码：
 * -> export interface AxiosRequestConfig<D = any> {
 * ->   url?: string;
 * ->   method?: Method | string;
 * ->   headers?: (RawAxiosRequestHeaders & MethodsHeaders) | AxiosHeaders;
 * ->   params?: any;
 * ->   data?: D;
 * -> }
 * -> export class Axios {
 * ->   request<T = any, R = AxiosResponse<T>, D = any>(config: AxiosRequestConfig<D>): Promise<R>;
 * ->   get<T = any, R = AxiosResponse<T>, D = any>(url: string, config?: AxiosRequestConfig<D>): Promise<R>;
 * ->   post<T = any, R = AxiosResponse<T>, D = any>(url: string, data?: D, config?: AxiosRequestConfig<D>): Promise<R>;
 * -> }
 * -> export interface AxiosInstance extends Axios {
 * ->   <T = any, R = AxiosResponse<T>, D = any>(config: AxiosRequestConfig<D>): Promise<R>;
 * -> }
 * -> // 测试四：Axios请求MockJS数据（二次封装）Api优化版之经过Pinia状态管理库版
 * -> // 请求成功: {transitional: {…}, adapter: Array(3), transformRequest: Array(1), transformResponse: Array(1), timeout: 5000, …}
 * -> // 响应成功: {data: {…}, status: 200, statusText: 'OK', headers: AxiosHeaders, config: {…}, …}
 * -> // Pinia [return] data: {code: 200, data: 'Admin Token', message: 'success', okay: true}
 * -> import useUserStore from '@/store/modules/user';
 * -> let userStore = useUserStore();
 * -> let userLoginRequestApiPinia = async () => {
 * ->   let data = {
 * ->     username: "admin",
 * ->     password: "111111",
 * ->   };
 * ->   let headers = {
 * ->     "Content-Type": "application/json", // JSON格式
 * ->     // 'Content-Type': 'application/x-www-form-urlencoded', // 表单格式
 * ->   };
 * ->   let res = await userStore.userLoginAction(data, headers);
 * ->   console.log("Pinia [return] data:", res);
 * -> };
 * -> const userInfoRequestApiPinia = async () => {
 * ->   let params = {
 * ->     username: "admin",
 * ->     password: "111111",
 * ->   };
 * ->   let headers = {
 * ->     "X-Requested-With": "XMLHttpRequest",
 * ->     token: "Admin Token",
 * ->   };
 * ->   let res = await userStore.userInfoAction(params, headers);
 * ->   console.log("Pinia [return] data:", res);
 * -> };
 * -> const userLogoutRequestApiPinia = async () => {
 * ->   let params = {
 * ->     username: "admin",
 * ->     password: "111111",
 * ->   };
 * ->   let headers = {
 * ->     "X-Requested-With": "XMLHttpRequest",
 * ->     token: "Admin Token",
 * ->   };
 * ->   let res = await userStore.userLogoutAction(params, headers);
 * ->   console.log("Pinia [return] data:", res);
 * -> };
 * 
 * 第034步：表单校验（Form） 
 * -> 公共：./src/utils/common.ts
 * -> // 封装一个函数:获取一个结果:当前早上|上午|下午|晚上
 * -> export const getTimeStr = () => {
 * ->   let message = "";
 * ->   // 通过内置构造函数Date
 * ->   let hours = new Date().getHours();
 * ->   // 情况的判断
 * ->   if (hours <= 9) {
 * ->     message = "早上好，欢迎登录！";
 * ->   } else if (hours <= 12) {
 * ->     message = "上午好，欢迎登录！";
 * ->   } else if (hours <= 18) {
 * ->     message = "下午好，欢迎登录！";
 * ->   } else {
 * ->     message = "晚上好，欢迎登录！";
 * ->   }
 * ->   return message;
 * -> };
 * -> 页面：./src/views/login/index.vue
 * -> <template>
 * ->   <div class="login_container">
 * ->     <el-row>
 * ->       <el-col :span="12" :xs="0"></el-col>
 * ->       <el-col :span="12" :xs="24">
 * ->         <el-form class="login_from" :model="loginForm" :rules="rules" ref="loginFormRef">
 * ->           <el-form-item prop="logo"><el-link type="primary" :underline="false">(^_^)</el-link></el-form-item>
 * ->           <el-form-item prop="title"><el-link type="primary" :underline="false">深圳引望智能技术有限公司</el-link></el-form-item>
 * ->           <el-form-item prop="username"><el-input :prefix-icon="User" v-model="loginForm.username"></el-input></el-form-item>
 * ->           <el-form-item prop="password"><el-input :prefix-icon="Lock" type="password" show-password v-model="loginForm.password"></el-input></el-form-item>
 * ->           <el-form-item prop="operation"><el-button :loading="loading" :disabled="disabled" class="login_btn" type="primary" icon="User" @click="userLoginPiniaApiRequest">登录</el-button></el-form-item>
 * ->         </el-form>
 * ->       </el-col>
 * ->     </el-row>
 * ->   </div>
 * -> </template>
 * -> <script setup lang="ts">
 * -> import { User, Lock } from "@element-plus/icons-vue";
 * -> import { reactive, ref } from "vue";
 * -> import { ElNotification, FormInstance, FormRules } from "element-plus";
 * -> // 00.表单对象Ref
 * -> let loginFormRef = ref<FormInstance>();
 * -> // 01.表单对象
 * -> let loginForm = reactive({
 * ->   username: "admin",
 * ->   password: "111111",
 * -> });
 * -> // 02.表单校验
 * -> // let rules = {
 * -> //   username: [
 * -> //     {
 * -> //       required: true,
 * -> //       max: 12,
 * -> //       min: 5,
 * -> //       message: "用户名不能小于5或超过12个字母或数字",
 * -> //       trigger: ["blur", "change"],
 * -> //     },
 * -> //   ],
 * -> //   password: [
 * -> //     {
 * -> //       required: true,
 * -> //       max: 15,
 * -> //       min: 6,
 * -> //       message: "用户名不能超过12个字母或数字",
 * -> //       trigger: ["blur", "change"],
 * -> //     },
 * -> //   ],
 * -> // };
 * -> // 02.自定义表单校验
 * -> let validatorUsername = (rule: any, value: any, callback: any) => {
 * ->   // console.log('账号校验规则:', rule);
 * ->   if (!value) {
 * ->     return callback(new Error("账号是必填项"));
 * ->   }
 * ->   setTimeout(() => {
 * ->     if (!value) {
 * ->       callback(new Error("账号是必填项"));
 * ->     } else {
 * ->       if (value.length < 5) {
 * ->         callback(new Error("账号必须大于五位"));
 * ->       }
 * ->       if (value.length > 18) {
 * ->         callback(new Error("账号不能大于十八位"));
 * ->       }
 * ->       callback();
 * ->     }
 * ->   }, 1000);
 * -> };
 * -> let validatorPassword = (rule: any, value: any, callback: any) => {
 * ->   // console.log('密码校验规则:', rule);
 * ->   if (value === "") {
 * ->     callback(new Error("Please input the password"));
 * ->   } else {
 * ->     if (loginForm.password !== "") {
 * ->       if (!loginFormRef.value) return;
 * ->       loginFormRef.value.validateField("checkPass");
 * ->     }
 * ->     callback();
 * ->   }
 * -> };
 * -> let rules = reactive<FormRules<typeof loginForm>>({
 * ->   username: [
 * ->     {
 * ->       validator: validatorUsername,
 * ->       trigger: ["blur", "change"],
 * ->     },
 * ->   ],
 * ->   password: [
 * ->     {
 * ->       validator: validatorPassword,
 * ->       trigger: ["blur", "change"],
 * ->     },
 * ->   ],
 * -> });
 * -> // 03.表单接口
 * -> import useUserStore from "@/store/modules/user";
 * -> import { useRoute, useRouter } from "vue-router";
 * -> import { getTimeStr } from "@/utils/common";
 * -> let userStore = useUserStore();
 * -> let $route = useRoute();
 * -> let $router = useRouter();
 * -> let loading = ref(false);
 * -> let disabled = ref(false);
 * -> let userLoginPiniaApiRequest = async () => {
 * ->   loading.value = true;
 * ->   disabled.value = true;
 * ->   console.log('表单对象Ref1:', loginFormRef.value);
 * ->   console.log('表单对象Ref2:', loginFormRef.value?.validate);
 * ->   console.log('表单对象Ref3:', loginFormRef.value?.validate());
 * ->   console.log('表单对象1:', loginForm);
 * ->   // 正确示范
 * ->   let a = await loginFormRef.value?.validate();
 * ->   console.log("校验结果a:", a);
 * ->   // 正确示范
 * ->   let b = await loginFormRef.value?.validate((valid) => {
 * ->     if (valid) {
 * ->       console.log('校验已通过b:', valid);
 * ->     } else {
 * ->       console.log('校验不通过b:', valid);
 * ->     }
 * ->   });
 * ->   console.log("校验结果b:", b);
 * ->   // 正确示范
 * ->   let c = await loginFormRef.value?.validate((arg0: boolean) => {
 * ->     if (arg0) {
 * ->       console.log("校验已通过c:", arg0);
 * ->     } else {
 * ->       console.log("校验不通过c:", arg0);
 * ->     }
 * ->   });
 * ->   console.log("校验结果c:", c);
 * ->   // 错误示范(没有增加 await 字段!异步,所有信息执行完成,才会执行打印)
 * ->   let d = loginFormRef.value?.validate((arg0: boolean) => {
 * ->     if (arg0) {
 * ->       console.log('校验已通过d:', arg0);
 * ->     } else {
 * ->       console.log('校验不通过d:', arg0);
 * ->     }
 * ->   });
 * ->   console.log("校验结果d:", d);
 * ->   let headers = {
 * ->     "Content-Type": "application/json", // JSON格式
 * ->   };
 * ->   await userStore.userLoginAction(loginForm, headers)
 * ->     .then((res) => {
 * ->       console.log("Api [return] data:", res);
 * ->       if (res.code === 200) {
 * ->         let flag = $route.query.redirect;
 * ->         console.log('带了路径的哟:', flag);
 * ->         if (flag) {
 * ->           $router.push({ path: flag });
 * ->         } else {
 * ->           $router.push({ path: "/" });
 * ->         }
 * ->         ElNotification({ type: "success", title: res.data, message: getTimeStr() });
 * ->         loading.value = false;
 * ->         disabled.value = false;
 * ->       }
 * ->     })
 * ->     .catch((error) => {
 * ->       ElNotification({ type: "error", title: "登录失败", message: error.message });
 * ->       loading.value = false;
 * ->       disabled.value = false;
 * ->     });
 * -> };
 * -> </script>
 * -> <style scoped lang="scss">
 * -> .login_container {
 * ->   width: 100%;
 * ->   height: 100vh;
 * ->   background-color: slategrey;
 * ->   background-image: url("@/assets/images/background.jpg");
 * ->   background-size: cover;
 * -> }
 * -> .login_from {
 * ->   position: relative;
 * ->   width: 60%;
 * ->   top: 30vh;
 * ->   background-image: url("@/assets/images/login_form.png");
 * ->   background-size: cover;
 * ->   padding: 40px;
 * ->   .el-link {
 * ->     font-size: 20px;
 * ->     color: white;
 * ->   }
 * ->   .login_btn {
 * ->     width: 100%;
 * ->   }
 * -> }
 * -> </style>
 * 
 * 第037步：布局（Layout）
 * -> 路由记录对象：./src/api/common/index.ts
 * -> 路由记录对象：./src/api/common/types.ts
 * -> import type { RouteRecordRaw } from "vue-router";
 * -> export interface CommonStore {
 * ->   token: string | null;
 * ->   routeRecordRaw: RouteRecordRaw[];
 * ->   avatar: string | null;
 * ->   username: string | null;
 * ->   buttons: [];
 * -> }
 * -> 路由记录对象：./src/store/modules/common.ts
 * -> import { CommonStore } from "@/api/common/types";
 * -> import { constantRoutes } from "@/router/routes";
 * -> import { defineStore } from "pinia";
 * -> const useCommonStore = defineStore("Common", {
 * ->   state: (): CommonStore => {
 * ->     return {
 * ->       token: "",
 * ->       routeRecordRaw: constantRoutes,
 * ->       username: "",
 * ->       avatar: "",
 * ->       buttons: [],
 * ->     };
 * ->   },
 * ->   actions: {},
 * ->   getters: {},
 * -> });
 * -> export default useCommonStore;
 * -> 折叠：./src/store/modules/layout-collapse.ts
 * -> // [门店仓库] LayoutCollapse 布局折叠组件配置仓库
 * -> import { defineStore } from "pinia";
 * -> let useLayoutCollapseStore = defineStore("LayoutCollapse", {
 * ->   state: () => {
 * ->     return {
 * ->       // 折叠属性(导航栏左侧breadcrumb.vue组件控制layout.vue组件页面折叠功能)
 * ->       collapse: false
 * ->     }
 * ->   }
 * -> });
 * -> export default useLayoutCollapseStore;
 * -> 页面：./src/views/layout/logo/index.vue
 * -> <template>
 * ->   <div>
 * ->     <h1>Logo page...</h1>
 * ->   </div>
 * -> </template>
 * -> <script setup lang="ts"></script>
 * -> <style scoped lang="scss"></style>
 * -> 页面：./src/views/layout/slider/index.vue
 * -> <template>
 * ->   <div>
 * ->     <h1>Slider page...</h1>
 * ->   </div>
 * -> </template>
 * -> <script setup lang="ts"></script>
 * -> <style scoped lang="scss"></style>
 * -> 页面：./src/views/layout/tabbar/index.vue
 * -> <template>
 * ->   <div>
 * ->     <h1>Tabbar page...</h1>
 * ->   </div>
 * -> </template>
 * -> <script setup lang="ts"></script>
 * -> <style scoped lang="scss"></style>
 * -> 页面：./src/views/layout/domain/index.vue
 * -> <template>
 * ->   <div class="domain_container">
 * ->     <!-- 0. RouterView(渲染路由组件) && Transition(过渡动画组件) && Component(动态组件) || KeepAlive(动态切换缓存组件) -->
 * ->     <!-- 
 * ->     <router-view v-slot="{ Component, route }">
 * ->       <keep-alive>
 * ->         <transition :name="route.meta.transition || 'fade'" mode="out-in" appear>
 * ->           <component :is="Component" key="route.path" view-prop="value"></component>
 * ->         </transition>
 * ->       </keep-alive>
 * ->     </router-view>
 * ->     -->
 * ->     <!-- 0. RouterView插槽 + Transition过渡动画 + Component动态组件 -->
 * ->     <!-- 1. TODO -> 第二层：在容器中添加路由组件占位符（路由：path: "/home"）到页面：component: () => import("@/views/home/index.vue") -->
 * ->     <!-- 1.1. RouterView 插槽：显示用户所在的当前路由的组件 -->
 * ->     <!-- 1.2. v-slot="{ Component, route }" 接收 prop 的默认插槽并解构 -->
 * ->     <router-view v-slot="{ Component, route }">
 * ->       <!-- 2. 组件间过渡 -->
 * ->       <!-- 2.1. 内置组件：Transition name="fade" mode="out-in" appear -->
 * ->       <transition name="route.meta.transition || fade" mode="out-in" appear>
 * ->         <!-- 2.2. 动态组件 -->
 * ->         <component :is="Component" :key="route.path" view-prop="value"></component>
 * ->       </transition>
 * ->     </router-view>
 * ->   </div>
 * -> </template>
 * -> <script setup lang="ts"></script>
 * -> <style scoped lang="scss">
 * -> .fade-enter-active, .fade-leave-active {
 * ->   transition: opacity 0.5s ease;
 * -> }
 * -> .fade-enter-from, .fade-leave-to {
 * ->   opacity: 0;
 * -> }
 * -> </style>
 * -> 页面：./src/views/layout/index.vue
 * -> <template>
 * ->   <div class="layout">
 * ->     <div class="layout_slider" :class="{ dynamicStyle: layoutCollapseStore.collapse }">
 * ->       <Logo :collapse="layoutCollapseStore.collapse"></Logo>
 * ->       <!-- 套一层（Scrollbar 滚动条组件） -->
 * ->       <el-scrollbar class="scrollbar">
 * ->         <!-- 第一层（Menu 菜单组件） -->
 * ->         <!-- mode	          菜单展示模式（mode="horizontal" | "vertical"） -->
 * ->         <!-- collapse	      是否水平折叠收起菜单（仅在 mode="vertical" 时可用） -->
 * ->         <!-- default-active	页面加载时默认激活菜单的索引（index） -->
 * ->         <el-menu mode="vertical" :collapse="layoutCollapseStore.collapse" :default-active="$route.path" active-text-color="#ffd04b" background-color="#545c64" text-color="#fff">
 * ->           <Slider :routeRecordRaw="commonStore.routeRecordRaw"></Slider>
 * ->         </el-menu>
 * ->       </el-scrollbar>
 * ->     </div>
 * ->     <div class="layout_tabbar" :class="{ dynamicStyle: layoutCollapseStore.collapse }">
 * ->       <!-- 1.Collapse   折叠面板组件 -->
 * ->       <!-- 2.Breadcrumb 面包屑的组件 -->
 * ->       <!-- 3.Button     刷新按钮组件 -->
 * ->       <!-- 4.Button     全屏按钮组件 -->
 * ->       <!-- 5.Dropdown   下拉菜单组件 -->
 * ->       <Tabbar></Tabbar>
 * ->     </div>
 * ->     <div class="layout_domain" :class="{ dynamicStyle: layoutCollapseStore.collapse }">
 * ->       <Domain></Domain>
 * ->     </div>
 * ->   </div>
 * -> </template>
 * -> <script lang="ts">
 * -> export default {
 * ->   // [方式 1]<script> 双标签兼容所有版本(推荐)
 * ->   name: "Layout",
 * -> };
 * -> </script>
 * -> <script setup lang="ts">
 * -> import { nextTick, watch } from "vue";
 * -> import { useRoute } from "vue-router";
 * -> import Logo from "./logo/index.vue";
 * -> import Slider from "./slider/index.vue";
 * -> import Tabbar from "./tabbar/index.vue";
 * -> import Domain from "./domain/index.vue";
 * -> import useLayoutCollapseStore from "@/store/modules/layout-collapse.ts";
 * -> import useCommonStore from "@/store/modules/common.ts";
 * -> // [方式 2]Vue 3.3+ 官方推荐(‌defineOptions 宏):npm install unplugin-vue-define-options -D
 * -> // defineOptions({
 * -> //   name: 'Layout'
 * -> // });
 * -> const layoutCollapseStore = useLayoutCollapseStore();
 * -> const commonStore = useCommonStore();
 * -> const $route = useRoute();
 * -> watch(
 * ->   () => layoutCollapseStore.collapse,
 * ->   (val) => {
 * ->     console.log('折叠之前:', val);
 * ->     nextTick(() => {
 * ->       console.log('折叠之后:', val);
 * ->     });
 * ->   }
 * -> );
 * -> </script>
 * -> <style scoped lang="scss">
 * -> .layout {
 * ->   width: 100%;
 * ->   height: 100vh;
 * ->   // 侧边栏
 * ->   .layout_slider {
 * ->     width: 200px;
 * ->     height: 100vh;
 * ->     background-color: #545c64;
 * ->     transition: all 0.5s;
 * ->     .scrollbar {
 * ->       width: 100%;
 * ->       height: calc(100vh - 56px);
 * ->       .el-menu {
 * ->         border-right: none;
 * ->       }
 * ->     }
 * ->     // Expand && Collapse
 * ->     &.dynamicStyle {
 * ->       width: 64px;
 * ->     }
 * ->   }
 * ->   // 导航栏
 * ->   .layout_tabbar {
 * ->     position: fixed;
 * ->     top: 0px;
 * ->     left: 200px;
 * ->     width: calc(100% - 200px);
 * ->     height: 56px;
 * ->     background-color: #c6e1ff;
 * ->     transition: all 0.5s;
 * ->     // Expand && Collapse
 * ->     &.dynamicStyle {
 * ->       left: 64px;
 * ->       width: calc(100% - 64px);
 * ->     }
 * ->   }
 * ->   // 主体区
 * ->   .layout_domain {
 * ->     position: absolute;
 * ->     top: 56px;
 * ->     left: 200px;
 * ->     width: calc(100% - 200px);
 * ->     height: calc(100vh - 56px);
 * ->     padding: 8px;
 * ->     background-color: #ecf5ff;
 * ->     overflow: auto;
 * ->     transition: all 0.5s;
 * ->     // Expand && Collapse
 * ->     &.dynamicStyle {
 * ->       top: 56px;
 * ->       left: 64px;
 * ->       width: calc(100% - 64px);
 * ->     }
 * ->   }
 * -> }
 * -> </style>
 * -> 补全路由：./src/router/routes.ts
 * -> export const constantRoutes = [
 * ->   {
 * ->     path: "/",
 * ->     component: () => import("@/views/layout/index.vue"), // 路由懒加载import
 * ->     name: "Layout", // 命名空间name，命名路由（路由权限控制）
 * ->     meta: {
 * ->       title: "布局页面",
 * ->       icon: "HomeFilled",
 * ->       hidden: false,
 * ->     },
 * ->     redirect: "/home",
 * ->     children: [
 * ->       {
 * ->         path: "/home",
 * ->         component: () => import("@/views/home/index.vue"), // 路由懒加载import
 * ->         name: "Home", // 命名空间name，命名路由（路由权限控制）
 * ->         meta: {
 * ->           title: "公司首页",
 * ->           icon: "HomeFilled",
 * ->           hidden: false,
 * ->         },
 * ->       },
 * ->     ],
 * ->   },
 * ->   {
 * ->     path: "/acl",
 * ->     component: () => import("@/views/layout/index.vue"),
 * ->     name: "SystemManagement",
 * ->     meta: {
 * ->       title: "权限管理",
 * ->       icon: "UserFilled",
 * ->       hidden: false,
 * ->     },
 * ->     redirect: "/acl/user",
 * ->     children: [
 * ->       {
 * ->         path: "/acl/user",
 * ->         component: () => import("@/views/acl/user/index.vue"),
 * ->         name: "Lock",
 * ->         meta: {
 * ->           title: "用户管理",
 * ->           icon: "User",
 * ->           hidden: false,
 * ->         },
 * ->       },
 * ->       {
 * ->         path: "/acl/role",
 * ->         component: () => import("@/views/acl/role/index.vue"),
 * ->         name: "Role",
 * ->         meta: {
 * ->           title: "角色管理",
 * ->           icon: "View",
 * ->           hidden: false,
 * ->         },
 * ->       },
 * ->       {
 * ->         path: "/acl/menu",
 * ->         component: () => import("@/views/acl/menu/index.vue"),
 * ->         name: "Menu",
 * ->         meta: {
 * ->           title: "菜单管理",
 * ->           icon: "MoreFilled",
 * ->           hidden: false,
 * ->         },
 * ->       },
 * ->     ],
 * ->   },
 * ->   {
 * ->     path: "/product",
 * ->     component: () => import("@/views/layout/index.vue"),
 * ->     name: "ProductManagement",
 * ->     meta: {
 * ->       title: "商品管理",
 * ->       icon: "ShoppingBag",
 * ->       hidden: false,
 * ->     },
 * ->     redirect: "/product/brand",
 * ->     children: [
 * ->       {
 * ->         path: "/product/brand",
 * ->         component: () => import("@/views/product/brand/index.vue"),
 * ->         name: "Brand",
 * ->         meta: {
 * ->           title: "品牌管理",
 * ->           icon: "Goods",
 * ->           hidden: false,
 * ->         },
 * ->       },
 * ->       {
 * ->         path: "/product/property",
 * ->         component: () => import("@/views/product/property/index.vue"),
 * ->         name: "Property",
 * ->         meta: {
 * ->           title: "属性管理",
 * ->           icon: "MagicStick",
 * ->           hidden: false,
 * ->         },
 * ->       },
 * ->       {
 * ->         path: "/product/sku",
 * ->         component: () => import("@/views/product/sku/index.vue"),
 * ->         name: "Sku",
 * ->         meta: {
 * ->           title: "SKU管理",
 * ->           icon: "Sell",
 * ->           hidden: false,
 * ->         },
 * ->       },
 * ->       {
 * ->         path: "/product/spu",
 * ->         component: () => import("@/views/product/spu/index.vue"),
 * ->         name: "Spu",
 * ->         meta: {
 * ->           title: "SPU管理",
 * ->           icon: "SoldOut",
 * ->           hidden: false,
 * ->         },
 * ->       },
 * ->     ],
 * ->   },
 * ->   {
 * ->     path: "/login",
 * ->     component: () => import("@/views/login/index.vue"), // 路由懒加载import
 * ->     name: "Login", // 命名空间name，命名路由（路由权限控制）
 * ->     meta: {
 * ->       title: "登录页面",
 * ->       icon: "User",
 * ->       hidden: true,
 * ->     },
 * ->   },
 * ->   {
 * ->     path: "/screen",
 * ->     component: () => import("@/views/screen/index.vue"),
 * ->     name: "Screen",
 * ->     meta: {
 * ->       title: "数据大屏",
 * ->       icon: "SwitchFilled",
 * ->       hidden: false,
 * ->     },
 * ->   },
 * ->   {
 * ->     path: "/404",
 * ->     component: () => import("@/views/404/index.vue"),
 * ->     name: "404",
 * ->     meta: {
 * ->       title: "错误页面",
 * ->       icon: "ElementPlus",
 * ->       hidden: true,
 * ->     },
 * ->   },
 * ->   {
 * ->     path: "/:pathMatch(.*)*",
 * ->     component: () => import("@/views/404/index.vue"),
 * ->     name: "Any",
 * ->     meta: {
 * ->       title: "任意页面",
 * ->       icon: "Eleme",
 * ->       hidden: true,
 * ->     },
 * ->   },
 * -> ];
 * -> 页面：./src/views/acl/user/index.vue
 * -> 页面：./src/views/acl/role/index.vue
 * -> 页面：./src/views/acl/menu/index.vue
 * -> 页面：./src/views/product/brand/index.vue
 * -> 页面：./src/views/product/property/index.vue
 * -> 页面：./src/views/product/sku/index.vue
 * -> 页面：./src/views/product/spu/index.vue
 * -> 页面：./src/views/screen/index.vue
 * -> 页面：./src/views/404/index.vue
 * 
 * 第38步：补疤托疤（Logo.vue/Slider.vue/Tabbar.vue）
 * -> 页面：./src/views/layout/logo/index.vue
 * -> <template>
 * ->   <div class="logo">
 * ->     <img src="@/assets/vite.svg" alt="logo" sizes="" srcset="">
 * ->     <p align="left" v-show="!collapse">刘伟伟</p>
 * ->   </div>
 * -> </template>
 * -> <script lang="ts">
 * -> export default {
 * ->   // [方式 1]<script> 双标签兼容所有版本(推荐)
 * ->   name: "Logo",
 * -> };
 * -> </script>
 * -> <script setup lang="ts">
 * -> // [方式 2]Vue 3.3+ 官方推荐(‌defineOptions 宏):npm install unplugin-vue-define-options -D
 * -> // defineOptions({
 * -> //   name: 'Logo'
 * -> // });
 * -> defineProps(["collapse"]);
 * -> </script>
 * -> <style scoped lang="scss">
 * -> .logo {
 * ->   width: 100%;
 * ->   height: 56px;
 * ->   color: white;
 * ->   display: flex;
 * ->   align-items: center;
 * ->   img {
 * ->     margin-left: 4px;
 * ->     width: 56px;
 * ->     height: 56px;
 * ->     padding: 12px;
 * ->   }
 * ->   p {
 * ->     font-size: 16px;
 * ->     transition: all 0.5s;
 * ->   }
 * -> }
 * -> </style>
 * -> 页面：./src/views/layout/slider/index.vue
 * -> <template>
 * ->   <template v-for="(item) in props.routeRecordRaw" :key="item.path">
 * ->     <!-- 没有子路由 -->
 * ->     <template v-if="!item.children && !item.meta.hidden">
 * ->       <el-menu-item :index="item.path" @click="gotoRoute">
 * ->         <template #title>
 * ->           <el-icon><component :is="item.meta.icon"></component></el-icon>
 * ->           <span>{{ item.meta.title }}</span>
 * ->         </template>
 * ->       </el-menu-item>
 * ->     </template>
 * ->     <!-- 已有子路由并且只有一个子路由吧 -->
 * ->     <template v-if="item.children && item.children.length == 1 && !item.children[0].meta.hidden">
 * ->       <el-menu-item :index="item.children[0].path" @click="gotoRoute">
 * ->         <template #title>
 * ->           <el-icon><component :is="item.children[0].meta.icon"></component></el-icon>
 * ->           <span>{{ item.children[0].meta.title }}</span>
 * ->         </template>
 * ->       </el-menu-item>
 * ->     </template>
 * ->     <!-- 已有子路由并且已有多个子路由吧 -->
 * ->     <template v-if="item.children && item.children.length > 1">
 * ->       <el-sub-menu :index="item.path">
 * ->         <template #title>
 * ->           <el-icon><component :is="item.meta.icon"></component></el-icon>
 * ->           <span>{{ item.meta.title }}</span>
 * ->         </template>
 * ->         <!-- ★★★递归调用★★★ -->
 * ->         <Slider :routeRecordRaw="item.children"></Slider>
 * ->       </el-sub-menu>
 * ->     </template>
 * ->   </template>
 * -> </template>
 * -> <!-- <script lang="ts">
 * -> export default {
 * ->   // ★★★递归组件★★★（组件内调用自己时必须定义name）方式 1：双 <script> 标签，兼容所有版本（别人推荐）
 * ->   name: "Slider",
 * -> };
 * -> </script> -->
 * -> <script setup lang="ts">
 * -> // import { onMounted } from "vue";
 * -> import { RouteLocationAsPathGeneric, RouteLocationAsRelativeGeneric, useRouter } from "vue-router";
 * -> // 低版本安装兼容插件（Vue < 3.3）
 * -> // npm install unplugin-vue-define-options -D
 * -> // ★★★递归组件★★★（组件内调用自己时必须定义name）方式 2：defineOptions 宏（‌Vue 3.3+ 官方推荐‌）
 * -> defineOptions({
 * ->   name: 'Slider'
 * -> });
 * -> const props = defineProps(["routeRecordRaw"]);
 * -> const $router = useRouter();
 * -> const gotoRoute = (vc: { index: string | RouteLocationAsRelativeGeneric | RouteLocationAsPathGeneric }) => {
 * ->   // console.log('vc is ->', vc);
 * ->   $router.push(vc.index);
 * -> };
 * -> </script>
 * -> <style scoped lang="scss"></style>
 * -> 页面：./src/views/layout/tabbar/breadcrumb/index.vue
 * -> <template>
 * ->   <div>
 * ->     <h1>bread crumb page...</h1>
 * ->   </div>
 * -> </template>
 * -> <script setup lang="ts"></script>
 * -> <style scoped lang="scss"></style>
 * -> 页面：./src/views/layout/tabbar/dropdown/index.vue
 * -> <template>
 * ->   <div>
 * ->     <h1>drop down page...</h1>
 * ->   </div>
 * -> </template>
 * -> <script setup lang="ts"></script>
 * -> <style scoped lang="scss"></style>
 * -> 页面：./src/views/layout/tabbar/index.vue
 * -> <template>
 * ->   <div class="tabbar">
 * ->     <div class="tabbar_left">
 * ->       <!-- 1.Collapse   折叠面板组件 -->
 * ->       <!-- 2.Breadcrumb 面包屑的组件 -->
 * ->       <Breadcrumb></Breadcrumb>
 * ->     </div>
 * ->     <div class="tabbar_right">
 * ->       <!-- 3.Button     刷新按钮组件 -->
 * ->       <!-- 4.Button     全屏按钮组件 -->
 * ->       <!-- 5.Dropdown   下拉菜单组件 -->
 * ->       <!-- 6.Popover    弹出框的组件 -->
 * ->       <!-- 7.Image      头像的组件吧 -->
 * ->       <Dropdown></Dropdown>
 * ->     </div>
 * ->   </div>
 * -> </template>
 * -> <script lang="ts">
 * -> export default {
 * ->   // [方式 1]<script> 双标签兼容所有版本(推荐)
 * ->   name: "Tabbar",
 * -> };
 * -> </script>
 * -> <script setup lang="ts">
 * -> import Breadcrumb from "./breadcrumb/index.vue";
 * -> import Dropdown from "./dropdown/index.vue";
 * -> // [方式 2]Vue 3.3+ 官方推荐(‌defineOptions 宏):npm install unplugin-vue-define-options -D
 * -> // defineOptions({
 * -> //   name: 'Tabbar'
 * -> // });
 * -> </script>
 * -> <style scoped lang="scss">
 * -> .tabbar {
 * ->   width: 100%;
 * ->   height: 100%;
 * ->   display: flex;
 * ->   justify-content: space-between;
 * ->   .tabbar_left {
 * ->     display: flex;
 * ->     align-items: center;
 * ->     margin-left: 16px;
 * ->   }
 * ->   .tabbar_right {
 * ->     display: flex;
 * ->     align-items: center;
 * ->     margin-right: 16px;
 * ->   }
 * -> }
 * -> </style>
 * 
 * 第038步：折叠功能与刷新功能（Collapse与Refresh）
 * -> // 【折叠功能】
 * -> 定义：./src/store/modules/layout-collapse.ts
 * -> import { defineStore } from "pinia";
 * -> let useLayoutCollapseStore = defineStore("LayoutCollapse", {
 * ->   state: () => {
 * ->     return {
 * ->       // 折叠属性(导航栏左侧breadcrumb.vue组件控制layout.vue组件页面折叠功能)
 * ->       collapse: false
 * ->     }
 * ->   }
 * -> });
 * -> export default useLayoutCollapseStore;
 * -> // 上层：./src/store/index.ts
 * -> import { createPinia } from "pinia";
 * -> const pinia = createPinia();
 * -> export default pinia;
 * -> 控制：./src/views/layout/tabbar/breadcrumb/index.vue
 * -> <template>
 * ->   <!-- 1.Collapse   折叠面板组件 -->
 * ->   <el-icon @click="layoutCollapseFunc">
 * ->     <component :is="layoutCollapseStore.collapse ? 'DArrowRight' : 'DArrowLeft'"></component>
 * ->   </el-icon>
 * ->   <!-- 2.Breadcrumb 面包屑的组件 -->
 * ->   <el-breadcrumb separator-icon="ArrowRight">
 * ->     <el-breadcrumb-item v-for="(item, index) in $route.matched" :key="index" v-show="item.meta.title" :to="item.path"  :replace="true">
 * ->       <el-icon><component :is="item.meta.icon" /></el-icon>
 * ->       <span>{{ item.meta.title }}</span>
 * ->     </el-breadcrumb-item>
 * ->   </el-breadcrumb>
 * -> </template>
 * -> <script lang="ts">
 * -> export default {
 * ->   name: "Breadcrumb",
 * -> };
 * -> </script>
 * -> <script setup lang="ts">
 * -> import { useRoute } from "vue-router";
 * -> import useLayoutCollapseStore from "@/store/modules/layout-collapse.ts";
 * -> // 2.Breadcrumb 面包屑的组件
 * -> const $route = useRoute();
 * -> // 1.Collapse   折叠面板组件
 * -> const layoutCollapseStore = useLayoutCollapseStore();
 * -> const layoutCollapseFunc = () => {
 * ->   layoutCollapseStore.collapse = !layoutCollapseStore.collapse;
 * -> };
 * -> </script>
 * -> <style scoped lang="scss"></style>
 * -> // 上层：./src/views/layout/tabbar/index.vue
 * -> <template>
 * ->   <div class="tabbar">
 * ->     <div class="tabbar_left">
 * ->       <!-- 1.Collapse   折叠面板组件 -->
 * ->       <!-- 2.Breadcrumb 面包屑的组件 -->
 * ->       <Breadcrumb></Breadcrumb>
 * ->     </div>
 * ->     <div class="tabbar_right">
 * ->       <!-- 3.Button     刷新按钮组件 -->
 * ->       <!-- 4.Button     全屏按钮组件 -->
 * ->       <!-- 5.Dropdown   下拉菜单组件 -->
 * ->       <!-- 6.Popover    弹出框的组件 -->
 * ->       <!-- 7.Image      头像的组件吧 -->
 * ->       <Dropdown></Dropdown>
 * ->     </div>
 * ->   </div>
 * -> </template>
 * -> <script lang="ts">
 * -> export default {
 * ->   // [方式 1]<script> 双标签兼容所有版本(推荐)
 * ->   name: "Tabbar",
 * -> };
 * -> </script>
 * -> <script setup lang="ts">
 * -> import Breadcrumb from "./breadcrumb/index.vue";
 * -> import Dropdown from "./dropdown/index.vue";
 * -> </script>
 * -> <style scoped lang="scss">
 * -> .tabbar {
 * ->   width: 100%;
 * ->   height: 100%;
 * ->   display: flex;
 * ->   justify-content: space-between;
 * ->   .tabbar_left {
 * ->     display: flex;
 * ->     align-items: center;
 * ->     margin-left: 16px;
 * ->   }
 * ->   .tabbar_right {
 * ->     display: flex;
 * ->     align-items: center;
 * ->     margin-right: 16px;
 * ->   }
 * -> }
 * -> </style>
 * -> 使用：./src/views/layout/index.vue
 * -> <template>
 * ->   <div class="layout">
 * ->     <div class="layout_slider" :class="{ dynamicStyle: layoutCollapseStore.collapse }">
 * ->       <Logo :collapse="layoutCollapseStore.collapse"></Logo>
 * ->       <el-scrollbar class="scrollbar">
 * ->         <el-menu mode="vertical" :collapse="layoutCollapseStore.collapse" :default-active="$route.path" active-text-color="#ffd04b" background-color="#545c64" text-color="#fff">
 * ->           <Slider :routeRecordRaw="commonStore.routeRecordRaw"></Slider>
 * ->         </el-menu>
 * ->       </el-scrollbar>
 * ->     </div>
 * ->     <div class="layout_tabbar" :class="{ dynamicStyle: layoutCollapseStore.collapse }">
 * ->       <Tabbar></Tabbar>
 * ->     </div>
 * ->     <div class="layout_domain" :class="{ dynamicStyle: layoutCollapseStore.collapse }">
 * ->       <Domain></Domain>
 * ->     </div>
 * ->   </div>
 * -> </template>
 * -> <script lang="ts">
 * -> export default {
 * ->   // [方式 1]<script> 双标签兼容所有版本(推荐)
 * ->   name: "Layout",
 * -> };
 * -> </script>
 * -> <script setup lang="ts">
 * -> import { nextTick, watch } from "vue";
 * -> import { useRoute } from "vue-router";
 * -> import Logo from "./logo/index.vue";
 * -> import Slider from "./slider/index.vue";
 * -> import Tabbar from "./tabbar/index.vue";
 * -> import Domain from "./domain/index.vue";
 * -> import useLayoutCollapseStore from "@/store/modules/layout-collapse.ts";
 * -> import useCommonStore from "@/store/modules/common.ts";
 * -> // [方式 2]Vue 3.3+ 官方推荐(‌defineOptions 宏):npm install unplugin-vue-define-options -D
 * -> // defineOptions({
 * -> //   name: 'Layout'
 * -> // });
 * -> const layoutCollapseStore = useLayoutCollapseStore();
 * -> const commonStore = useCommonStore();
 * -> const $route = useRoute();
 * -> watch(
 * ->   () => layoutCollapseStore.collapse,
 * ->   (val) => {
 * ->     console.log('折叠之前:', val);
 * ->     nextTick(() => {
 * ->       console.log('折叠之后:', val);
 * ->     });
 * ->   }
 * -> );
 * -> </script>
 * -> <style scoped lang="scss">
 * -> .layout {
 * ->   width: 100%;
 * ->   height: 100vh;
 * ->   // 侧边栏
 * ->   .layout_slider {
 * ->     width: 200px;
 * ->     height: 100vh;
 * ->     background-color: #545c64;
 * ->     transition: all 0.5s;
 * ->     .scrollbar {
 * ->       width: 100%;
 * ->       height: calc(100vh - 56px);
 * ->       .el-menu {
 * ->         border-right: none;
 * ->       }
 * ->     }
 * ->     // Expand && Collapse
 * ->     &.dynamicStyle {
 * ->       width: 64px;
 * ->     }
 * ->   }
 * ->   // 导航栏
 * ->   .layout_tabbar {
 * ->     position: fixed;
 * ->     top: 0px;
 * ->     left: 200px;
 * ->     width: calc(100% - 200px);
 * ->     height: 56px;
 * ->     background-color: #c6e1ff;
 * ->     transition: all 0.5s;
 * ->     // Expand && Collapse
 * ->     &.dynamicStyle {
 * ->       left: 64px;
 * ->       width: calc(100% - 64px);
 * ->     }
 * ->   }
 * ->   // 主体区
 * ->   .layout_domain {
 * ->     position: absolute;
 * ->     top: 56px;
 * ->     left: 200px;
 * ->     width: calc(100% - 200px);
 * ->     height: calc(100vh - 56px);
 * ->     padding: 8px;
 * ->     background-color: #ecf5ff;
 * ->     overflow: auto;
 * ->     transition: all 0.5s;
 * ->     // Expand && Collapse
 * ->     &.dynamicStyle {
 * ->       top: 56px;
 * ->       left: 64px;
 * ->       width: calc(100% - 64px);
 * ->     }
 * ->   }
 * -> }
 * -> </style>
 * -> // 【刷新功能】
 * -> 定义：./src/store/modules/layout-refresh.ts
 * -> import { defineStore } from "pinia";
 * -> let useLayoutRefreshStore = defineStore("LayoutCollapse", {
 * ->   state: () => {
 * ->     return {
 * ->       // 刷新属性(导航栏右侧setting.vue组件控制layout.vue组件中main.vue主体组件页面刷新功能)
 * ->       refresh: false
 * ->     }
 * ->   }
 * -> });
 * -> export default useLayoutRefreshStore;
 * -> // 上层：./src/store/index.ts
 * -> import { createPinia } from "pinia";
 * -> const pinia = createPinia();
 * -> export default pinia;
 * -> 控制：./src/views/layout/tabbar/dropdown/index.vue
 * -> <template>
 * ->   <!-- 3.Button     刷新按钮组件 -->
 * ->   <el-button size="small" icon="Refresh" circle @click="layoutRefreshFunc"></el-button>
 * ->   <!-- 4.Button     全屏按钮组件 -->
 * ->   <el-button size="small" icon="FullScreen" circle @click="documentFullscreenFunc"></el-button>
 * ->   <!-- 6.Popover    弹出框的组件 -->
 * ->   <el-popover placement="bottom" title="主题设置" :width="300" trigger="hover">
 * ->     <el-form>
 * ->       <el-form-item label="主题颜色">
 * ->         <el-color-picker @change="setColor" v-model="color" size="small" show-alpha :predefine="predefineColors" />
 * ->       </el-form-item>
 * ->       <el-form-item label="暗黑模式">
 * ->         <el-switch @change="changeDark" v-model="dark" class="mt-2" style="margin-left: 24px" inline-prompt active-icon="MoonNight" inactive-icon="Sunny" />
 * ->       </el-form-item>
 * ->     </el-form>
 * ->     <template #reference>
 * ->       <el-button size="small" icon="Setting" circle></el-button>
 * ->     </template>
 * ->   </el-popover>
 * ->   <!-- 7.Image      头像的组件吧 -->
 * ->   <el-image style="width: 24px; height: 24px; margin: 0px 10px; border-radius: 50%;" :src="userStore.avatar"></el-image>
 * ->   <!-- 5.Dropdown   下拉菜单组件 -->
 * ->   <el-dropdown>
 * ->     <span class="el-dropdown-link">
 * ->       {{ userStore.username }}
 * ->       <el-icon class="el-icon--right"><arrow-down /></el-icon>
 * ->     </span>
 * ->     <template #dropdown>
 * ->       <el-dropdown-menu>
 * ->         <el-dropdown-item @click="userLogoutData">退出登录</el-dropdown-item>
 * ->       </el-dropdown-menu>
 * ->     </template>
 * ->   </el-dropdown>
 * -> </template>
 * -> <script lang="ts">
 * -> export default {
 * ->   // [方式 1]<script> 双标签兼容所有版本(推荐)
 * ->   name: "Dropdown",
 * -> };
 * -> </script>
 * -> <script setup lang="ts">
 * -> import { ref } from "vue";
 * -> import { useRouter } from "vue-router";
 * -> import { userLogoutApi } from "@/api/user/index.ts";
 * -> import useUserStore from "@/store/modules/user";
 * -> import useLayoutRefreshStore from "@/store/modules/layout-refresh";
 * -> let layoutRefreshStore = useLayoutRefreshStore();
 * -> let userStore = useUserStore();
 * -> let $router = useRouter();
 * -> let dark = ref<boolean>(false);
 * -> const layoutRefreshFunc = () => {
 * ->   layoutRefreshStore.refresh = !layoutRefreshStore.refresh;
 * -> };
 * -> const documentFullscreenFunc = () => {
 * ->   let result = document.fullscreenElement;
 * ->   if (result) {
 * ->     document.exitFullscreen();
 * ->   } else {
 * ->     document.documentElement.requestFullscreen();
 * ->   }
 * -> };
 * -> let REMOVE_TOKEN = () => {
 * ->   localStorage.removeItem("TOKEN");
 * -> };
 * -> const userLogoutData = async () => {
 * ->   await userLogoutApi(
 * ->     {
 * ->       username: "admin",
 * ->       password: "111111",
 * ->     },
 * ->     {
 * ->       "X-Requested-With": "XMLHttpRequest",
 * ->       token: "Admin Token",
 * ->     }
 * ->   )
 * ->     .then((success) => {
 * ->       console.log("Api [return] data:", success);
 * ->       if (success.code === 200) {
 * ->         REMOVE_TOKEN();
 * ->         $router.push("/login");
 * ->       }
 * ->     })
 * ->     .catch((error) => {
 * ->       console.log("Api [return] data:", error);
 * ->     });
 * -> };
 * -> const color = ref("rgba(255, 69, 0, 0.68)");
 * -> const predefineColors = ref([
 * ->   "#ff4500",
 * ->   "#ff8c00",
 * ->   "#ffd700",
 * ->   "#90ee90",
 * ->   "#00ced1",
 * ->   "#1e90ff",
 * ->   "#c71585",
 * ->   "rgba(255, 69, 0, 0.68)",
 * ->   "rgb(255, 120, 0)",
 * ->   "hsv(51, 100, 98)",
 * ->   "hsva(120, 40, 94, 0.5)",
 * ->   "hsl(181, 100%, 37%)",
 * ->   "hsla(209, 100%, 56%, 0.73)",
 * ->   "#c7158577",
 * -> ]);
 * -> const changeDark = () => {
 * ->   let html = document.documentElement;
 * ->   dark.value ? (html.className = "dark") : (html.className = "");
 * -> };
 * -> const setColor = () => {
 * ->   const html = document.documentElement;
 * ->   html.style.setProperty("--el-color-primary", color.value);
 * -> };
 * -> </script>
 * -> <style scoped lang="scss"></style>
 * -> // 上层：./src/views/layout/tabbar/index.vue
 * -> <template>
 * ->   <div class="tabbar">
 * ->     <div class="tabbar_left">
 * ->       <!-- 1.Collapse   折叠面板组件 -->
 * ->       <!-- 2.Breadcrumb 面包屑的组件 -->
 * ->       <Breadcrumb></Breadcrumb>
 * ->     </div>
 * ->     <div class="tabbar_right">
 * ->       <!-- 3.Button     刷新按钮组件 -->
 * ->       <!-- 4.Button     全屏按钮组件 -->
 * ->       <!-- 5.Dropdown   下拉菜单组件 -->
 * ->       <!-- 6.Popover    弹出框的组件 -->
 * ->       <!-- 7.Image      头像的组件吧 -->
 * ->       <Dropdown></Dropdown>
 * ->     </div>
 * ->   </div>
 * -> </template>
 * -> <script lang="ts">
 * -> export default {
 * ->   // [方式 1]<script> 双标签兼容所有版本(推荐)
 * ->   name: "Tabbar",
 * -> };
 * -> </script>
 * -> <script setup lang="ts">
 * -> import Breadcrumb from "./breadcrumb/index.vue";
 * -> import Dropdown from "./dropdown/index.vue";
 * -> </script>
 * -> <style scoped lang="scss">
 * -> .tabbar {
 * ->   width: 100%;
 * ->   height: 100%;
 * ->   display: flex;
 * ->   justify-content: space-between;
 * ->   .tabbar_left {
 * ->     display: flex;
 * ->     align-items: center;
 * ->     margin-left: 16px;
 * ->   }
 * ->   .tabbar_right {
 * ->     display: flex;
 * ->     align-items: center;
 * ->     margin-right: 16px;
 * ->   }
 * -> }
 * -> </style>
 * -> 使用：./src/views/layout/domain/index.vue
 * -> <template>
 * ->   <!-- 路由组件出口的位置 -->
 * ->   <router-view v-slot="{ Component }">
 * ->     <!-- 2. 组件间过渡 -->
 * ->     <!-- 2.1. Transition内置组件，可以帮助你制作基于状态变化的过渡和动画，name="fade" 为过渡效果命名，这个 fade 过渡的 class 应该是这样：.fade-enter-active,.fade-leave-active,.fade-enter-from,.fade-leave-to -->
 * ->     <transition name="fade">
 * ->       <!-- 2.2. Component动态组件 -->
 * ->       <!-- 渲染layout一级路由组件的子路由 -->
 * ->       <!-- 动态组件：渲染一个【元组件】为动态组件。依 is 的值，来决定哪个组件被渲染。 -->
 * ->       <!-- 动态的加载，渲染相应的组件。因为它的is属性指向组件componentId。 -->
 * ->       <!-- 属性： -->
 * ->       <!-- 1. is - string | ComponentDefinition | ComponentConstructor -->
 * ->       <!-- 2. inline-template - boolean -->
 * ->       <!-- 用法： -->
 * ->       <!-- 1. 动态组件由 vm 实例的 `componentId` property控制。 -->
 * ->       <!-- <component :is="componentId"></component> -->
 * ->       <!-- 2. 也能够渲染注册过的组件或 prop 传入的组件。 -->
 * ->       <!-- <component :is="$options.components.child"></component> -->
 * ->       <component :is="Component" v-if="refreshFlag" />
 * ->     </transition>
 * ->   </router-view>
 * -> </template>
 * -> <script lang="ts">
 * -> export default {
 * ->   // [方式 1]<script> 双标签兼容所有版本(推荐)
 * ->   name: "Domain",
 * -> };
 * -> </script>
 * -> <script setup lang="ts">
 * -> import { watch, ref, nextTick } from "vue";
 * -> import useLayoutRefreshStore from "@/store/modules/layout-refresh";
 * -> const layoutRefreshStore = useLayoutRefreshStore();
 * -> const refreshFlag = ref(true);
 * -> watch(
 * ->   () => layoutRefreshStore.refresh,
 * ->   () => {
 * ->     //点击刷新按钮:路由组件销毁
 * ->     refreshFlag.value = false;
 * ->     console.log("刷新之前:", refreshFlag.value);
 * ->     nextTick(() => {
 * ->       refreshFlag.value = true;
 * ->       console.log("刷新之后:", refreshFlag.value);
 * ->     });
 * ->   }
 * -> );
 * -> </script>
 * -> <style scoped>
 * -> .fade-enter-from {
 * ->   opacity: 0;
 * ->   transform: scale(0);
 * -> }
 * -> .fade-enter-active {
 * ->   transition: all 0.5s;
 * -> }
 * -> .fade-enter-to {
 * ->   opacity: 1;
 * ->   transform: scale(1);
 * -> }
 * -> </style>
 * 
 */




import { createApp } from "vue";

// import "@/style.scss";
// npm install sass sass-loader -D
import "@/styles/index.scss";
// npm install element-plus -S
import ElementPlus from "element-plus";
import "element-plus/dist/index.css";
import zhCn from "element-plus/es/locale/lang/zh-cn";
// npm install @element-plus/icons-vue -S
import * as ElementPlusIconsVue from "@element-plus/icons-vue";
// npm install vite-plugin-svg-icons -D
// 插件所需配置代码
import "virtual:svg-icons-register";
import SvgIcons from '@/components/index.ts';
// npm install vue-router -S
import router from "@/router/index.ts";
// npm install pinia -S
import pinia from "@/store/index.ts";

import App from "@/App.vue";
let app = createApp(App);

app.use(ElementPlus, {
  locale: zhCn,
});
for (let [key, component] of Object.entries(ElementPlusIconsVue)) {
  app.component(key, component);
}
app.use(SvgIcons);
app.use(router);
app.use(pinia);

app.mount("#app");
